using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;


namespace PW.ApplicationBlocks.Cryptography
{
   /// <summary>
   /// Easy use of HMACSHA1.
   /// </summary>
   public sealed class HMACSHA1Util {
      private static readonly HMACSHA1 s_sha1 = new HMACSHA1();
      private static readonly Encoding s_encoding = Encoding.UTF8;
      private static bool s_keySet = false;

      private HMACSHA1Util() {}

      /// <summary>
      /// Sets the key used in hashing.
      /// </summary>
      /// <param name="key">Key to set.</param>
      public static void SetKey(byte[] key) {
         if (key == null) {
            throw new ArgumentNullException("key");
         }

         s_sha1.Key = key;
         s_keySet = true;
      }

      /// <summary>
      /// Compute the hash of a buffer.
      /// </summary>
      /// <param name="buffer">Buffer to compute.</param>
      /// <returns>Computed hash with length of 20 bytes.</returns>
      public static byte[] ComputeHash(byte[] buffer) {
         if (! s_keySet) {
            throw new InvalidOperationException("Key must be set before you do this.");
         }
         if (buffer == null) {
            throw new ArgumentNullException("buffer");
         }

         return s_sha1.ComputeHash(buffer);
      }

      /// <summary>
      /// Compute the hash of a buffer.
      /// </summary>
      /// <param name="buffer">Buffer to compute.</param>
      /// <param name="offset">Offset</param>
      /// <param name="count">Count</param>
      /// <returns>Computed hash with length of 20 bytes.</returns>
      public static byte[] ComputeHash(byte[] buffer, int offset, int count) {
         if (! s_keySet) {
            throw new InvalidOperationException("Key must be set before you do this.");
         }
         if (buffer == null) {
            throw new ArgumentNullException("buffer");
         }

         return s_sha1.ComputeHash(buffer, offset, count);
      }

      /// <summary>
      /// Compute the hash of a stream.
      /// </summary>
      /// <param name="stream">Stream to compute.</param>
      /// <returns>Computed hash with length of 20 bytes.</returns>
      public static byte[] ComputeHash(Stream stream) {
         if (! s_keySet) {
            throw new InvalidOperationException("Key must be set before you do this.");
         }
         if (stream == null) {
            throw new ArgumentNullException("stream");
         }

         return s_sha1.ComputeHash(stream);
      }

      /// <summary>
      /// Compute the hash of a text.
      /// </summary>
      /// <param name="text">Text to compute.</param>
      /// <returns>Computed hash with length of 20 bytes.</returns>
      public static byte[] ComputeHash(string text) {
         if (! s_keySet) {
            throw new InvalidOperationException("Key must be set before you do this.");
         }
         if (text == null) {
            throw new ArgumentNullException("text");
         }

         byte[] buffer = s_encoding.GetBytes(text);

         return s_sha1.ComputeHash(buffer);
      }

      /// <summary>
      /// Compute the hash of a buffer.
      /// </summary>
      /// <param name="buffer">Buffer to compute.</param>
      /// <returns>Computed hash with length of 28 chars.</returns>
      public static string ComputeHashToBase64(byte[] buffer) {
         if (! s_keySet) {
            throw new InvalidOperationException("Key must be set before you do this.");
         }
         if (buffer == null) {
            throw new ArgumentNullException("buffer");
         }

         byte[] hash = s_sha1.ComputeHash(buffer);

         return Convert.ToBase64String(hash);
      }

      /// <summary>
      /// Compute the hash of a buffer.
      /// </summary>
      /// <param name="buffer">Buffer to compute.</param>
      /// <param name="offset">Offset</param>
      /// <param name="count">Count</param>
      /// <returns>Computed hash with length of 28 chars.</returns>
      public static string ComputeHashToBase64(byte[] buffer, int offset, int count) {
         if (! s_keySet) {
            throw new InvalidOperationException("Key must be set before you do this.");
         }
         if (buffer == null) {
            throw new ArgumentNullException("buffer");
         }

         byte[] hash = s_sha1.ComputeHash(buffer, offset, count);

         return Convert.ToBase64String(hash);
      }

      /// <summary>
      /// Compute the hash of a stream.
      /// </summary>
      /// <param name="stream">Stream to compute.</param>
      /// <returns>Computed hash with length of 28 chars.</returns>
      public static string ComputeHashToBase64(Stream stream) {
         if (! s_keySet) {
            throw new InvalidOperationException("Key must be set before you do this.");
         }
         if (stream == null) {
            throw new ArgumentNullException("stream");
         }

         byte[] hash = s_sha1.ComputeHash(stream);

         return Convert.ToBase64String(hash);
      }

      /// <summary>
      /// Compute the hash of a text.
      /// </summary>
      /// <param name="text">Text to compute.</param>
      /// <returns>Computed hash with length of 28 chars.</returns>
      public static string ComputeHashToBase64(string text) {
         if (! s_keySet) {
            throw new InvalidOperationException("Key must be set before you do this.");
         }
         if (text == null) {
            throw new ArgumentNullException("text");
         }

         byte[] hash = ComputeHash(text);

         return Convert.ToBase64String(hash);
      }
   }
}